package com.lizard.schedule.manager;

import android.text.TextUtils;

import com.android.volley.VolleyError;
import com.lizard.schedule.log.ScheduleLog;
import com.lizard.schedule.manager.observer.ObserverIfaces;
import com.lizard.schedule.manager.observer.ObserverManager;
import com.lizard.schedule.net.http.consts.ReturnCode;
import com.lizard.schedule.net.http.request.HttpRequestManager;
import com.lizard.schedule.net.http.request.HttpResponseListener;
import com.lizard.schedule.net.http.request.bean.ScheduleSync.ScheduleSyncItem;
import com.lizard.schedule.net.http.request.bean.ScheduleSync.ScheduleSyncRequest;
import com.lizard.schedule.net.http.response.bean.LoginResponse;
import com.lizard.schedule.net.http.response.bean.ScheduleSyncResponse;
import com.lizard.schedule.net.http.response.bean.ThirdLoginResponse;
import com.lizard.schedule.net.http.response.bean.ThirdVerifyResponse;
import com.lizard.schedule.persistence.db.bean.Schedule;
import com.lizard.schedule.persistence.db.bean.ScheduleVersion;
import com.lizard.schedule.persistence.db.table.ScheduleTbl;
import com.lizard.schedule.persistence.db.table.ScheduleVersionTbl;
import com.lizard.schedule.persistence.sharedpreferences.SyncTimePref;
import com.lizard.schedule.utils.TimeUtil;
import com.lizard.schedule.wear.Sender;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 用于同步计划
 * 同步采用队列的模式进行，避免多处同时同步引发的一系列问题
 * 同步完将自动替换数据库内容并通知观察者，界面上需要得到相关同步回调的地方请注册观察者接口
 * <p>
 * 与server同步步骤方案：
 * 1、client调用同步接口
 * 2、server若发现client传的pversion不是最新，则将最新pversion以及大于client的pversion的计划列表返回，client将最新数据更新到本地数据库后重新执行步骤1
 * 3、server若发现client传的pversion是最新，则将client传的数据列表更新到数据库并返回pversion，
 */
public class ScheduleSyncManager {

    private final String TAG = ScheduleSyncManager.class.getSimpleName();

    private static ScheduleSyncManager instance;

    /**
     * sync队列
     */
    private static Queue<SyncTask> mSyncTaskQueue = new LinkedBlockingQueue<SyncTask>();

    /**
     * 标记是否正在同步
     */
    private boolean mSyncing = false;

    private ScheduleSyncManager() {
    }

    public static ScheduleSyncManager getInstance() {
        if (instance == null) {
            synchronized (ScheduleSyncManager.class) {
                if (instance == null) {
                    instance = new ScheduleSyncManager();
                }
            }
        }
        return instance;
    }

    /**
     * 加入队列进行同步
     *
     * @param listener
     */
    public void sync(ObserverIfaces.SyncListener listener) {
        if (TextUtils.isEmpty(AccountManager.getInstance().getUsername())) { // 非注册用户
            return;
        }

        SyncTask syncTask = new SyncTask();
        syncTask.listener = listener;

        mSyncTaskQueue.add(syncTask);
        pull();
    }

    /**
     * 拉取任务开始同步
     */
    private void pull() {
        if (!mSyncing) {
            SyncTask syncTask = mSyncTaskQueue.poll();
            if (syncTask != null) {
                mSyncing = true;    // 开始同步
                handleSync(syncTask);
            }
        }
    }

    /**
     * 处理同步
     */
    private void handleSync(final SyncTask syncTask) {

        final String username = AccountManager.getInstance().getUsername();
        final String usernameMd5L16 = AccountManager.getInstance().getmUsernameMD5L16();

        if (TextUtils.isEmpty(username)) {
            clear();
            return;
        }

        // 本地计划版本号
        ScheduleVersion localScheduleVersion = ScheduleVersionTbl.getInstacne().select(username);
        final int localPversion = localScheduleVersion == null ?
                ScheduleVersion.VERSION_DEFAULT : localScheduleVersion.getVersion();

        // 本地未同步的数据
        final List<Schedule> unsyncedSchedules = ScheduleTbl.getInstance().selectUnsubmitedSchedules(usernameMd5L16);

        final ScheduleSyncRequest request = new ScheduleSyncRequest();
        request.setPversion(localPversion);
        List<ScheduleSyncItem> syncItems = new ArrayList<ScheduleSyncItem>();
        for (Schedule schedule : unsyncedSchedules) {
            ScheduleSyncItem scheduleSyncItem = new ScheduleSyncItem();
            scheduleSyncItem.setPlanId(schedule.getServerId());
            scheduleSyncItem.setLocalId(schedule.getLocalId());
            scheduleSyncItem.setPlanTime(TimeUtil.transferToDateTime3(schedule.getBeginTime()));
            scheduleSyncItem.setPlanInfo(schedule.getContentText());
            scheduleSyncItem.setPlanStatus(schedule.getStatus() == Schedule.STATUS_FINISHED ? ScheduleSyncItem.PLAN_STATUS_FINISHED : ScheduleSyncItem.PLAN_STATUS_UNFINISHED);
            int operType;
            if (schedule.isDeleted()) {
                operType = ScheduleSyncItem.OPER_TYPE_DELETE;
            } else if (schedule.getServerId() == Schedule.SERVER_ID_DEFAULT) {
                // serverId还是默认值 说明server没有存在该条记录 则为添加操作
                operType = ScheduleSyncItem.OPER_TYPE_ADD;
            } else {
                operType = ScheduleSyncItem.OPER_TYPE_MODIFY;
            }
            scheduleSyncItem.setOperType(operType);
            scheduleSyncItem.setRepeat(schedule.getRepeat());
            syncItems.add(scheduleSyncItem);
        }
        request.setList(syncItems);

        HttpRequestManager.getInstance().request(request, new HttpResponseListener<ScheduleSyncResponse>() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                ScheduleLog.v(TAG, "sync on error response");

                // 网络错误处理
                handleSyncNetWorkError(syncTask);

            }

            @Override
            public void onResponse(final ScheduleSyncResponse response) {
                if (ReturnCode.SUCCESS.equals(response.getReturnCode())) {
                    if (ScheduleSyncResponse.OPER_RESULT_SUBMIT_SUCCESS == response.getOperResult()) {    // 提交成功
                        ScheduleLog.v(TAG, "sync submit success");
                        new Thread() {
                            public void run() {

                                if (!username.equals(AccountManager.getInstance().getUsername())) {
                                    mSyncing = false;
                                    pull();
                                    return;
                                }

                                // 更新scheduleVersion到本地DB
                                updateScheduleVersionToDB(response.getPversion());

                                // 将提交的数据的删除的数据从本地删除、其他数据submit状态设置为true
                                List<Integer> localIds = new ArrayList<Integer>();
                                if (unsyncedSchedules != null && unsyncedSchedules.size() > 0) {
                                    List<Integer> needDeleteLocalIds = new ArrayList<Integer>();
                                    for (Schedule schedule : unsyncedSchedules) {
                                        localIds.add(schedule.getLocalId());
                                        if (schedule.isDeleted()) {
                                            needDeleteLocalIds.add(schedule.getLocalId());
                                        }
                                    }
                                    ScheduleTbl.getInstance().delete(usernameMd5L16, needDeleteLocalIds);
                                    ScheduleTbl.getInstance().setSchedulesSubmited(usernameMd5L16, localIds);
                                }

                                // 新增的数据记录serverId到本地DB
                                Map<Integer, Long> serverIdMap = new HashMap<Integer, Long>();
                                if (response.getList() != null && response.getList().size() > 0) {
                                    for (ScheduleSyncItem scheduleSyncItem : response.getList()) {
                                        serverIdMap.put(scheduleSyncItem.getLocalId(), scheduleSyncItem.getPlanId());
                                    }
                                    ScheduleTbl.getInstance().batchUpdateServerId(usernameMd5L16, serverIdMap);
                                }

                                // 记录最后一次同步时间
                                SyncTimePref.getInstance().setLastSyncTime(username, System.currentTimeMillis());

                                // 通知提交成功
                                ObserverManager.getInstance().notifySyncListenerSyncSuccess(localIds, serverIdMap);
                                if (syncTask.listener != null) {
                                    syncTask.listener.syncSuccess(localIds, serverIdMap);
                                }

                                mSyncing = false;
                                pull();

                            }
                        }.start();
                    } else if (ScheduleSyncResponse.OPER_RESULT_NEED_SYNC == response.getOperResult()) {  // 客户端需同步
                        ScheduleLog.v(TAG, "sync new data");
                        new Thread() {
                            public void run() {

                                // 更新scheduleVersion到本地DB
                                updateScheduleVersionToDB(response.getPversion());

                                // 将server中的数据更新到本地
                                if (response.getList() != null && response.getList().size() > 0) {
                                    // 查询所有serverId存在sql的数据
                                    List<Long> serverIds = new ArrayList<Long>();
                                    for (ScheduleSyncItem scheduleSyncItem : response.getList()) {
                                        serverIds.add(scheduleSyncItem.getPlanId());
                                    }
                                    Map<Long, Schedule> existSchedules = ScheduleTbl.getInstance().selectByServerIds(usernameMd5L16, serverIds);

                                    // 需要新增的数据集
                                    List<Schedule> addList = new ArrayList<Schedule>();

                                    // 需要更新的数据集
                                    List<Schedule> updateList = new ArrayList<Schedule>();

                                    // 需要删除的数据集
                                    List<Schedule> deleteList = new ArrayList<Schedule>();

                                    for (ScheduleSyncItem scheduleSyncItem : response.getList()) {
                                        if (existSchedules.containsKey(scheduleSyncItem.getPlanId())) {
                                            if (ScheduleSyncItem.IS_DELETED_YES.equals(scheduleSyncItem.getIsDeleted())) { // 删除的数据
                                                deleteList.add(existSchedules.get(scheduleSyncItem.getPlanId()));
                                            } else { // 更新的数据
                                                Schedule schedule = existSchedules.get(scheduleSyncItem.getPlanId());
                                                schedule.setBeginTime(TimeUtil.transferToTimeMills2(scheduleSyncItem.getPlanTime()));
                                                schedule.setContentText(scheduleSyncItem.getPlanInfo());
                                                schedule.setStatus(ScheduleSyncItem.PLAN_STATUS_FINISHED.equals(scheduleSyncItem.getPlanStatus()) ? Schedule.STATUS_FINISHED : Schedule.STATUS_UNFINISHED);
                                                schedule.setDeleted(false);
                                                schedule.setSubmited(true);
                                                schedule.setRepeat(scheduleSyncItem.getRepeat());
                                                updateList.add(schedule);
                                            }
                                        } else {
                                            if (!ScheduleSyncItem.IS_DELETED_YES.equals(scheduleSyncItem.getIsDeleted())) { // 新增的数据
                                                Schedule schedule = new Schedule(
                                                        ScheduleLocalIdGenerator.getInstance().getLocalId(),
                                                        scheduleSyncItem.getPlanInfo(),
                                                        scheduleSyncItem.getPlanId(),
                                                        TimeUtil.transferToTimeMills2(scheduleSyncItem.getPlanTime()),
                                                        ScheduleSyncItem.PLAN_STATUS_FINISHED.equals(scheduleSyncItem.getPlanStatus()) ? Schedule.STATUS_FINISHED : Schedule.STATUS_UNFINISHED,
                                                        false, true, scheduleSyncItem.getRepeat());
                                                addList.add(schedule);
                                            }
                                        }
                                    }

                                    // 本地数据库新增、删除、更新数据
                                    if (addList.size() > 0) {
                                        ScheduleTbl.getInstance().batchInsert(usernameMd5L16, addList);
                                    }
                                    if (updateList.size() > 0) {
                                        ScheduleTbl.getInstance().batchUpdate(usernameMd5L16, updateList);
                                    }
                                    if (deleteList.size() > 0) {
                                        List<Integer> deleteLocalIds = new ArrayList<Integer>();
                                        for (Schedule schedule : deleteList) {
                                            deleteLocalIds.add(schedule.getLocalId());
                                        }
                                        ScheduleTbl.getInstance().delete(usernameMd5L16, deleteLocalIds);
                                    }

                                    // 通知有新数据从server同步下来
                                    ObserverManager.getInstance().notifySyncListenerNewData(addList, updateList, deleteList);
                                    if (syncTask.listener != null) {
                                        syncTask.listener.newData(addList, updateList, deleteList);
                                    }

                                    // wear同步
                                    Sender.sendSchedules();
                                }

                                // 重新同步
                                handleSync(syncTask);

                            }
                        }.start();
                    } else { // 无提交且无需客户端更新
                        ScheduleLog.v(TAG, "sync newest");
                        // 记录最后一次同步时间
                        SyncTimePref.getInstance().setLastSyncTime(username, System.currentTimeMillis());

                        // 通知已是最新数据不需要同步
                        ObserverManager.getInstance().notifySyncListenerNoSync();
                        if (syncTask.listener != null) {
                            syncTask.listener.noSync();
                        }

                        mSyncing = false;
                        pull();
                    }
                } else if (ReturnCode.TOKEN_EXPIRE.equals(response.getReturnCode())) { // token过期
                    handleSyncTokenExpired(syncTask);
                } else {    // 其他同步失败的情况
                    handleSyncResponseFailed(syncTask, response);
                }
            }
        });
    }

    /**
     * 处理同步时token过期的问题
     *
     * @param syncTask
     */
    private void handleSyncTokenExpired(final SyncTask syncTask){
        if(AccountManager.getInstance().getThirdLoginType() > 0){
            AccountManager.getInstance().verifyAccountThird(new HttpResponseListener<ThirdVerifyResponse>() {
                @Override
                public void onErrorResponse(VolleyError volleyError) {

                    // 网络错误处理
                    handleSyncNetWorkError(syncTask);
                }

                @Override
                public void onResponse(ThirdVerifyResponse response) {
                    if (ReturnCode.SUCCESS.equals(response.getReturnCode())) {    // 成功
                        // 登录成功后重新同步
                        handleSync(syncTask);
                    }
                }
            });
        } else{
            AccountManager.getInstance().verifyAccount(new HttpResponseListener<LoginResponse>() {
                @Override
                public void onErrorResponse(VolleyError volleyError) {

                    // 网络错误处理
                    handleSyncNetWorkError(syncTask);
                }

                @Override
                public void onResponse(LoginResponse response) {
                    if (ReturnCode.SUCCESS.equals(response.getReturnCode())) {    // 成功
                        // 登录成功后重新同步
                        handleSync(syncTask);
                    }
                }
            });
        }
    }

    /**
     * 通用处理同步失败的其他响应码
     *
     * @param syncTask
     */
    private void handleSyncResponseFailed(SyncTask syncTask, ScheduleSyncResponse response) {
        if (syncTask.listener != null) {
            syncTask.listener.responseFailed(response.getReturnCode());
        }
        mSyncing = false;
        pull();
    }

    /**
     * 处理同步时网络错误
     *
     * @param syncTask
     */
    private void handleSyncNetWorkError(SyncTask syncTask) {
        // 通知网络错误
        ObserverManager.getInstance().notifySyncListenersNetWorkError();
        if (syncTask.listener != null) {
            syncTask.listener.netWorkError();
        }

        mSyncing = false;
        pull();
    }

    /**
     * 更新scheduleVersion到本地DB
     *
     * @param version
     */
    private void updateScheduleVersionToDB(int version) {
        ScheduleVersion scheduleVersion = ScheduleVersionTbl.getInstacne().select(AccountManager.getInstance().getUsername());
        if (scheduleVersion == null) {
            scheduleVersion = new ScheduleVersion();
            scheduleVersion.setUsername(AccountManager.getInstance().getUsername());
            scheduleVersion.setVersion(version);
            // 新增ScheduleVersion
            ScheduleVersionTbl.getInstacne().insert(scheduleVersion);
        } else {
            // 更新ScheduleVersion
            scheduleVersion.setVersion(version);
            ScheduleVersionTbl.getInstacne().update(scheduleVersion);
        }
    }

    /**
     * 清除队列
     */
    public void clear() {
        mSyncTaskQueue.clear();
        mSyncing = false;
    }

    /**
     * 队列中的对象所属类
     */
    class SyncTask {
        ObserverIfaces.SyncListener listener;
    }

}
